package com.xiaouyudeguang.common.utils;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.xiaouyudeguang.common.collect.SortMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * <p>
 * JSON操作工具
 * </p>
 *
 * @author 赵光
 * @since 2019年4月8日
 */
public class JsonObject {

    private static final Logger log = LoggerFactory.getLogger(JsonObject.class);

    private static ObjectMapper objectMapper;

    public static ObjectMapper mapper() {
        if (objectMapper == null) {
            objectMapper = new ObjectMapper();
            objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"));
            objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
            objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 反序列化时忽略不存在的属性
            objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); // 序列化时将LocalDateTime转换为时间戳格式
            objectMapper.registerModule(new JavaTimeModule());
        }
        return objectMapper;
    }

    public static ObjectWriter pretty() {
        return mapper().writer(new DefaultPrettyPrinter());
    }

    public static <T> void print(Object o) {
        try {
            System.out.println(pretty(o));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    public static String pretty(Object o) throws JsonProcessingException {
        return pretty().writeValueAsString(o);
    }

    public static String toJson(Object o) {
        try {
            if (o == null) {
                return null;
            }
            if (String.class != o.getClass() && o.getClass().getName().contains("java.lang")) {
                o = String.valueOf(o);
            }
            if (String.class != o.getClass()) {
                o = mapper().writeValueAsString(o);
            }
            return (String) o;
        } catch (Exception ignored) {

        }
        return null;
    }

    public static <T> JsonNode fromJson(byte[] str) {
        if (str == null) {
            return null;
        }
        try {
            return mapper().readTree(str);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public static <T> JsonNode fromJson(String str) {
        if (str == null) {
            return null;
        }
        try {
            return mapper().readTree(str);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public static <T> JsonNode fromJson(Object o) {
        if (o == null) {
            return null;
        }
        try {
            return mapper().readTree(mapper().writeValueAsString(o));
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    @SuppressWarnings("unchecked")
    public static <T> T fromJson(Object o, Class<T> cls) {
        if (o == null) {
            return null;
        }
        if (o.getClass() == cls) {
            return (T) o;
        }
        try {
            return mapper().readValue(toJson(o), cls);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public static <T> T fromJson(Object o, TypeReference<T> typeReference) {
        try {
            return o == null ? null : mapper().readValue(toJson(o), typeReference);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public static <T> T parseObject(Object o, Class<T> cls) {
        return fromJson(o, cls);
    }

    public static <T> T parseObject(Object o, TypeReference<T> typeReference) {
        try {
            return fromJson(o, typeReference);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public static <T> List<T> toList(Object o) {
        return fromJson(o, new TypeReference<List<T>>() {
        });
    }

    public static <T, V> List<V> toList(List<T> data, Class<V> vCls) {
        return fromJson(data, new TypeReference<List<V>>() {
        });
    }

    public static <T> Set<T> toSet(Object o) {
        return fromJson(o, new TypeReference<Set<T>>() {
        });
    }

    public static <T, V> Set<V> toList(Set<T> data, Class<V> vCls) {
        return fromJson(data, new TypeReference<Set<V>>() {
        });
    }

    public static <K, V> MapModel<K, V> toMap(Object o) {
        return fromJson(o, new TypeReference<MapModel<K, V>>() {
        });
    }

    public static JsonNode readTree(String content) {
        try {
            return mapper().readTree(content);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public static JsonNode readTree(Object obj) {
        try {
            return mapper().readTree(toJson(obj));
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public static Map<String, Object> getMap(JsonNode jsonNode) {
        Iterator<Map.Entry<String, JsonNode>> fields = jsonNode.fields();
        Map<String, Object> node = new HashMap<>();
        while (fields.hasNext()) {
            Map.Entry<String, JsonNode> entry = fields.next();
            node.put(entry.getKey(), entry.getValue().asText());
        }
        return node;
    }

    public static Map<String, Object> getMap(JsonNode jsonNode, String value) {
        return getMap(jsonNode.path(value));
    }

    public static ObjectNode createObjectNode() {
        return mapper().createObjectNode();
    }

    public static JsonNode createObjectNode(Object obj) {
        return readTree(obj);
    }

    public static ArrayNode createArrayNode() {
        return mapper().createArrayNode();
    }

    public static ArrayNode createArrayNode(List<? extends JsonNode> nodeList) {
        return mapper().createArrayNode().addAll(nodeList);
    }

    public static ArrayNode sort(ArrayNode arrayNode) {
        return sort(arrayNode, "sort");
    }

    public static ArrayNode sort(ArrayNode arrayNode, String sortKey) {
        SortMap<JsonNode> sortMap = new SortMap<>();
        for (JsonNode jsonNode : arrayNode) {
            sortMap.add(jsonNode.path(sortKey).asLong(100000), jsonNode);
        }
        return createArrayNode().addAll(sortMap.getValues());
    }

    public static JsonNode find(String text, String... paths) {
        return findByPath(readTree(text), 0, paths);
    }

    public static JsonNode find(JsonNode rootNode, String... paths) {
        return findByPath(rootNode, 0, paths);
    }

    private static JsonNode findByPath(JsonNode rootNode, int index, String... paths) {
        if (paths.length < index + 1 || !rootNode.has(paths[index])) {
            return null;
        }
        if (paths.length == index + 1) {
            return rootNode.get(paths[index]);
        }
        return findByPath(rootNode, index + 1, paths);
    }
}
